package com.ndbg.demo.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.apache.commons.codec.binary.Base64;

/**
 * @author chenXB
 * @createTime 2024年05月27日 16:43:00
 */
public class SCSignUtils {
    private static Logger logger = LoggerFactory.getLogger(SCSignUtils.class);
    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
    private static final String CHARSET = "utf-8";
    public static final String PUBLIC_KEY = "publicKey";
    public static final String PRIVATE_KEY = "privateKey";

    /**
     *
     * Description：map对象中存放公私钥 <br/>
     * Date：2019年7月24日 上午11:16:48　<br/>
     * Author：lean <br/>
     * @param keySize 字节数
     * @return
     * @throws Exception
     */
    public static Map<String, String> initKey(int keySize) throws Exception {
        //获得对象 KeyPairGenerator 参数 RSA 1024个字节
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(keySize);
        //通过对象 KeyPairGenerator 获取对象KeyPair
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //公私钥对象存入map中
        Map<String, String> keyMap = new HashMap<String, String>(2);
        keyMap.put(PUBLIC_KEY, Base64.encodeBase64String(publicKey.getEncoded()));
        keyMap.put(PRIVATE_KEY, Base64.encodeBase64String(privateKey.getEncoded()));
        return keyMap;
    }

    /**
     *
     * Description：MD5签名 <br/>
     * Date：2019年3月20日 下午3:02:14　<br/>
     * Author：lean <br/>
     * @param params 待签名参数对象
     * @param key 秘钥
     * @return
     */
    public static String signByMD5(Map<String, ? extends Object> params, String key) throws Exception{
        return DigestUtils.md5Hex(String.format("%s%s", createLinkString(paramsFilter(params)), key));
    }

    /**
     *
     * Description：MD5验签 <br/>
     * Date：2019年3月20日 下午3:02:44　<br/>
     * Author：lean <br/>
     * @param params 待验签参数对象
     * @param sign 签名串
     * @param key 秘钥
     * @return
     */
    public static boolean verifyByMD5(Map<String, ? extends Object> params, String sign, String key) throws Exception{
        String result = DigestUtils.md5Hex(String.format("%s%s", createLinkString(paramsFilter(params)), key));
        return result.equals(sign);
    }

    /**
     *
     * Description：RSA签名 <br/>
     * Date：2019年3月20日 下午3:03:27　<br/>
     * Author：lean <br/>
     * @param params 待签名对象
     * @param privateKey 签名私钥
     * @return
     * @throws Exception
     */
    public static String signByRSA(Map<String, ? extends Object> params, String privateKey) throws Exception {
        try {
            String content = createLinkString(paramsFilter(params));
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(CHARSET));
            byte[] signed = signature.sign();
            return Base64.encodeBase64String(signed);
        } catch (NoSuchAlgorithmException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (InvalidKeySpecException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (InvalidKeyException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (SignatureException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        }
    }

    /**
     *
     * Description：RSA签名 <br/>
     * Date：2019年3月20日 下午3:04:24　<br/>
     * Author：lean <br/>
     * @param content 待签名串
     * @param privateKey 签名私钥
     * @return
     * @throws Exception
     */
    public static String signByRSA(String content, String privateKey) throws Exception {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(CHARSET));
            byte[] signed = signature.sign();
            return Base64.encodeBase64String(signed);
        } catch (NoSuchAlgorithmException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (InvalidKeySpecException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (InvalidKeyException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (SignatureException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.warn("sign rsa error", e);
            throw new Exception("sign rsa error:"+e.getMessage());
        }
    }

    /**
     *
     * Description：获取待签名串 <br/>
     * Date：2019年3月20日 下午3:06:03　<br/>
     * Author：lean <br/>
     * @param params
     * @return
     * @throws Exception
     */
    public static String getSignByRSAContent(Map<String, ? extends Object> params) throws Exception {
        return createLinkString(paramsFilter(params));
    }

    /**
     *
     * Description：RSA验签 <br/>
     * Date：2019年3月20日 下午3:05:04　<br/>
     * Author：lean <br/>
     * @param params 待验签参数对象
     * @param sign 签名串
     * @param publicKey 验签公钥
     * @return
     * @throws Exception
     */
    public static boolean verifyByRSA(Map<String, ? extends Object> params, String sign, String publicKey) throws Exception {
        String content = createLinkString(paramsFilter(params));
        return verifyByRSA(content,sign,publicKey);
    }

    /**
     *
     * Description：RSA验签  <br/>
     * Date：2019年3月20日 下午3:05:37　<br/>
     * Author：lean <br/>
     * @param content 待验签串
     * @param sign 签名串
     * @param publicKey 验签公钥
     * @return
     * @throws Exception
     */
    public static boolean verifyByRSA(String content, String sign, String publicKey) throws Exception {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decodeBase64(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(CHARSET));
            return signature.verify(Base64.decodeBase64(sign));
        } catch (NoSuchAlgorithmException e) {
            logger.warn("vertify rsa error", e);
            throw new Exception("vertify rsa error:"+e.getMessage());
        } catch (InvalidKeySpecException e) {
            logger.warn("vertify rsa error", e);
            throw new Exception("vertify rsa error:"+e.getMessage());
        } catch (InvalidKeyException e) {
            logger.warn("vertify rsa error", e);
            throw new Exception("vertify rsa error:"+e.getMessage());
        } catch (SignatureException e) {
            logger.warn("vertify rsa error", e);
            throw new Exception("vertify rsa error:"+e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.warn("vertify rsa error", e);
            throw new Exception("vertify rsa error:"+e.getMessage());
        }
    }

    /**
     *
     * Description：获取待验签串 <br/>
     * Date：2019年3月20日 下午3:06:03　<br/>
     * Author：lean <br/>
     * @param params
     * @return
     * @throws Exception
     */
    public static String getVerifyByRSAContent(Map<String, ? extends Object> params) throws Exception {
        return createLinkString(paramsFilter(params));
    }

    /**
     *
     * Description： 参数过滤 <br/>
     * Date：2019年3月20日 下午2:51:50　<br/>
     * Author：lean <br/>
     * @param params
     * @return
     * @throws Exception
     */
    private static Map<String, String> paramsFilter(Map<String, ? extends Object> params) throws Exception{
        Map<String, String> result = new HashMap<>();
        if (params == null || params.size() == 0) {
            return result;
        }
        for (Map.Entry<String, ? extends Object> entry : params.entrySet()) {
            if (entry.getValue() == null || "sign".equals(entry.getKey())) {
                continue;
            }
            result.put(entry.getKey(), String.valueOf(entry.getValue()));
        }
        return result;
    }

    /**
     *
     * Description：参数拼接 <br/>
     * Date：2019年3月20日 下午2:52:12　<br/>
     * Author：lean <br/>
     * @param params
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static String createLinkString(Map<String, String> params) throws Exception{
        if (params == null) return "";
        ArrayList<String> keys = new ArrayList(params.keySet());
        Collections.sort(keys);
        StringBuffer sb = new StringBuffer();
        int keyLastNum = keys.size() - 1;
        for (int i = 0; i < keys.size(); ++i) {
            String key = keys.get(i);
            String value = String.valueOf(params.get(key));
            sb.append(key).append("=").append(value);
            if (i != keyLastNum) sb.append("&");
        }
        return sb.toString();
    }

    /**
     *
     * Description：MD5加密 <br/>
     * Date：2019年3月20日 下午3:01:52　<br/>
     * Author：lean <br/>
     * @param str
     * @return
     * @throws Exception
     */
    public static String md5(String str) throws Exception{
        return DigestUtils.md5Hex(str);
    }


    public static void main(String[] args) {
        String priKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKFYC6F87SZ/0i5PBac7NAqdFIae8V9ZGWZRjEr7Xjc84L07jKLSdcVFRv2SnwfxJYj4d+XnEuWmLPt+HzO0Pt7PUhrj8fib6dFL/Lm/+Z4D07JCkYwvWofawcdl6HL7KH+qhdu5V2zfB6Cd42QVcdudQMiexOCRKIPMB+aSTO3hAgMBAAECgYAwuvj4U1UFNcHzfqaEddsoAX17k3HvjGehgavP0dAI+vgd72hwnQQ5Cs2ANNKXgXwGvJTp+Tq55dDcPKGL6jq6cSnoWLPS9EoFU4zG69VxAUwn2pgm2Pbh85bn55h7MishD8vlYCUkszhQ7xGCwS6NGfiOWGnU78GvC/M9PBwnSQJBAOff1I6xpqebX/3wqgZ3kucI4AzF/NEpypVEfYTLaqmWp+PE5kbJQKo9f63TEhfZlk/vEeh7nfOK2e0Jl5wn7JcCQQCyIZPspS2BzxtvE8N2Z+Gw1i/9+a/kuaNzXYf6Z+EzQALveyFQ//lZHmZEial2Pqpu2LTc7aZWCROQ5iJF0jBHAkEA5EBY2/KSzBDOCIrcxCr26Ai7jLdk7H0IF7HpIUygfbp5IGSWDf5UcoDBa9oHyIR/S8ZZyZHOZYw8g/LaVHSUlwJAd2Lx1aVPPrUkWeoREwbEyNSFDd+O9+lDl0qUnEuLL+IzFXac+cDVydkKCd2J6mpNqEUt0vSkiv1CU9cqGUmqeQJBAL1xVgO7u7XWqGnnwKrg508i+lM7+3ojKsa+AKAfr42gjQC9zanax74JoqM5RBXkfRWYLy6p3D0kxjJz5d4AyE4=";
        String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChWAuhfO0mf9IuTwWnOzQKnRSGnvFfWRlmUYxK+143POC9O4yi0nXFRUb9kp8H8SWI+Hfl5xLlpiz7fh8ztD7ez1Ia4/H4m+nRS/y5v/meA9OyQpGML1qH2sHHZehy+yh/qoXbuVds3wegneNkFXHbnUDInsTgkSiDzAfmkkzt4QIDAQAB";
        try {
            //String msg = "123456";
            //System.out.println("明文：" + msg);
            Map reqMap = new HashMap();
            reqMap.put("version", "1.0.0");
            reqMap.put("timestamp", "20180508153040");	 //需使用新时间,不超过2小时
            reqMap.put("devId", "240533815");
            //注意业务参json串需base64
            reqMap.put("data", "eyJvdXRPcmRlck5vIjoiMjAyMTA3MTMxNjMyMDE2QUciLCJvdXRPcmRlckRhdGUiOiIyMDIxMDcxMyIsIm91dE9yZGVyVGltZSI6IjE2MzIwMSIsIm9yZGVyQW1vdW50IjoxLCJkaXNjb3VudEFtb3VudCI6MCwicGF5QW1vdW50IjoxLCJvcmRlclRpdGxlIjoi6K6i5Y2V5qCH6aKYNkFHIiwib3JkZXJEZXRhaWwiOiLorqLljZXor7TmmI42QUciLCJwYXNzYmFja1BhcmFtcyI6IumAmuefpeWPguaVsDZBRyIsInJlbWFyayI6IiIsIm9yZGVySXRlbXMiOlt7Iml0ZW1JbmRleCI6MSwib2ZmaWNlTm8iOiIwNTkxSFkyNzM4MzUxIiwiaXRlbUFtb3VudCI6MSwiaXRlbURpc2NvdW50QW1vdW50IjowLCJpdGVtUGF5QW1vdW50IjoxLCJtZXJjaGFudFR5cGUiOiIxMSIsInJlbWFyayI6IuWkh+azqDEifV0sInJldHVyblVybCI6Imh0dHA6Ly8xNzIuMTguMTY5LjcyOjg4ODgvbW9jay1tZXJjaGFudC9tb2NrL3BheWVkL3JldHVybiIsImNoYW5uZWxJbmZvIjoiIiwib3JkZXJFeHBpcmVkVGltZSI6IiJ9");
            String sign = SCSignUtils.signByRSA(reqMap, priKey);
            System.out.println("RSA私钥签名：" + sign);
            boolean result =  SCSignUtils.verifyByRSA(reqMap, sign, pubKey);
            System.out.println("使用公钥进行验签：" + (result ? "通过" : "不通过"));
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
